#include "wx/wxprec.h"
#include "wx/scrolwin.h"
#include "wx/gtk/private.h"

void wxScrollHelperNative::SetScrollbars( int pixelsPerUnitX, int pixelsPerUnitY,
    int noUnitsX, int noUnitsY,
    int xPos, int yPos,
    bool noRefresh ) {
  int xs, ys;
  GetViewStart( & xs, & ys );
  int old_x = m_xScrollPixelsPerLine * xs;
  int old_y = m_yScrollPixelsPerLine * ys;
  m_xScrollPixelsPerLine = pixelsPerUnitX;
  m_yScrollPixelsPerLine = pixelsPerUnitY;
  m_win->m_scrollBar[wxWindow::ScrollDir_Horz]->adjustment->value =
    m_xScrollPosition = xPos;
  m_win->m_scrollBar[wxWindow::ScrollDir_Vert]->adjustment->value =
    m_yScrollPosition = yPos;
  int w = noUnitsX * pixelsPerUnitX;
  int h = noUnitsY * pixelsPerUnitY;
  m_targetWindow->SetVirtualSize( w ? w : wxDefaultCoord,
                                  h ? h : wxDefaultCoord );
  if( m_targetWindow != m_win ) {
    AdjustScrollbars();
  }
  if( !noRefresh ) {
    int new_x = m_xScrollPixelsPerLine * m_xScrollPosition;
    int new_y = m_yScrollPixelsPerLine * m_yScrollPosition;
    m_targetWindow->ScrollWindow( old_x - new_x, old_y - new_y );
  }
}

void wxScrollHelperNative::DoAdjustScrollbar( GtkRange* range,
    int pixelsPerLine,
    int winSize,
    int virtSize,
    int *pos,
    int *lines,
    int *linesPerPage ) {
  int upper;
  int page_size;
  if( pixelsPerLine > 0 && winSize > 0 && winSize < virtSize ) {
    upper = ( virtSize + pixelsPerLine - 1 ) / pixelsPerLine;
    page_size = winSize / pixelsPerLine;
    *lines = upper;
    *linesPerPage = page_size;
  } else {
    // GtkRange won't allow upper == lower, so for disabled state use [0,1]
    //   with a page size of 1. This will also clamp position to 0.
    upper = 1;
    page_size = 1;
    *lines = 0;
    *linesPerPage = 0;
  }
  GtkAdjustment* adj = range->adjustment;
  adj->step_increment = 1;
  adj->page_increment =
    adj->page_size = page_size;
  gtk_range_set_range( range, 0, upper );
  // ensure that the scroll position is always in valid range
  if( *pos > *lines ) {
    *pos = *lines;
  }
}

void wxScrollHelperNative::AdjustScrollbars() {
  // this flag indicates which window has the scrollbars
  m_win->m_hasScrolling = m_xScrollPixelsPerLine != 0 ||
                          m_yScrollPixelsPerLine != 0;
  int vw, vh;
  m_targetWindow->GetVirtualSize( &vw, &vh );
  int w;
  m_targetWindow->GetClientSize( &w, NULL );
  DoAdjustHScrollbar( w, vw );
  int h;
  m_targetWindow->GetClientSize( NULL, &h );
  DoAdjustVScrollbar( h, vh );
  const int w_old = w;
  m_targetWindow->GetClientSize( &w, NULL );
  if( w != w_old ) {
    // It is necessary to repeat the calculations in this case to avoid an
    // observed infinite series of size events, involving alternating
    // changes in visibility of the scrollbars.
    // At this point, GTK+ has already queued a resize, which will cause
    // AdjustScrollbars() to be called again. If the scrollbar visibility
    // is not correct before then, yet another resize will occur, possibly
    // leading to an unending series if the sizes are just right.
    DoAdjustHScrollbar( w, vw );
    m_targetWindow->GetClientSize( NULL, &h );
    DoAdjustVScrollbar( h, vh );
  }
}

void wxScrollHelperNative::DoScroll( int orient,
                                     int pos,
                                     int pixelsPerLine,
                                     int *posOld ) {
  if( pos != -1 && pos != *posOld && pixelsPerLine ) {
    m_win->SetScrollPos( orient, pos );
    pos = m_win->GetScrollPos( orient );
    int diff = ( *posOld - pos ) * pixelsPerLine;
    m_targetWindow->ScrollWindow( orient == wxHORIZONTAL ? diff : 0,
                                  orient == wxHORIZONTAL ? 0 : diff );
    *posOld = pos;
  }
}

void wxScrollHelperNative::Scroll( int x_pos, int y_pos ) {
  wxCHECK_RET( m_targetWindow != 0, _T( "No target window" ) );
  DoScroll( wxHORIZONTAL, x_pos, m_xScrollPixelsPerLine, &m_xScrollPosition );
  DoScroll( wxVERTICAL, y_pos, m_yScrollPixelsPerLine, &m_yScrollPosition );
}
